#ifndef XSHADER_H
#define XSHADER_H
#include <d3d11_1.h>
#include <vector>
#include "Common.h"
#include "Texture.h"
#include "Common.h"
#include "Sampler.h"
/*
enum ShaderUniformType
{
	GVT_UNKNOWN,
	GVT_WORLD,
	GVT_VIEW,
	GVT_PROJECTION,
	GVT_WORLD_VIEW,
	GVT_WORLD_VIEW_PROJECTION,
	GVT_EYE_POSITION,
	GVT_LIGHT_AMBIENT_COLOR,
};
*/
enum UniformID
{
	SU_MATRIX_WORLD,
	SU_MATRIX_VIEW,
	SU_MATRIX_PROJECTION,
	SU_MATRIX_WORLD_VIEW,
	SU_MATRIX_WORLD_VIEW_PROJECTION,
	SU_TEXTURE0,
	SU_SAMPLER0,
	SU_COUNT
};
enum VaryingID
{
	SV_FLOAT3_POSITION,
	SV_FLOAT4_SV_POSITION,
	SV_FLOAT2_TEXCOORD0,
};
/*
union UniformData
{
	int intValue;
	float floatValue;
	float vec3fValue[3];
	float vec4fValue[4];
	float matrixValue[4][4];
};
*/
/*
struct ShaderUniform
{
	UINT type;
	int indexInBuffer;
	ShaderUniform()
	{
		this->type = 0;
		indexInBuffer = 0;
	}
	ShaderUniform(UINT type, int index = 0)
	{
		this->type = type;
		indexInBuffer = index;
	}
};
*/
struct ShaderDefine
{
	UINT id;
	char* name;
	std::vector<UINT> uniformList;
	std::vector<UINT> varyingList;
	ShaderDefine()
	{
		name  = NULL;
	}
	ShaderDefine(UINT iid, char* iname, std::vector<UINT> iuniform, std::vector<UINT> ivarying)
	{
		name  = NULL;
		id = iid;
		SafeCopyCHAR(&name, iname);
		uniformList = iuniform;
		varyingList = ivarying;
	}
	~ShaderDefine()
	{
		SafeDeleteArray(name);
	}
};
enum ShaderIDList
{
	SIMPLE_VS,
	SIMPLE_PS,
	TEXTURE1_DIFFUSE_VS,
	TEXTURE1_DIFFUSE_PS,
	TEXTURE1_DISTORTION_VS,
	TEXTURE1_DISTORTION_PS,
	SHADER_COUNT
};
struct UniformMap
{
	std::string name;
	UniformID type;
	int offset; //offset in uniformDataStore
	UniformMap(const char* iname, int ioffset)
	{
		name = iname;
		offset = ioffset;
		if(strcmp(iname, "u_world") == 0)
		{
			type = SU_MATRIX_WORLD;
		}
		else if(strcmp(iname, "u_view") == 0)
		{
			type = SU_MATRIX_VIEW;
		}
		else if(strcmp(iname, "u_projection") == 0)
		{
			type = SU_MATRIX_PROJECTION;
		}
		else if(strcmp(iname, "u_world_view") == 0)
		{
			type = SU_MATRIX_WORLD_VIEW;
		}
		else if(strcmp(iname, "u_world_view_projection") == 0)
		{
			type = SU_MATRIX_WORLD_VIEW_PROJECTION;
		}
		else
		{
			type = (UniformID)-1;
		}
	}
};

class Shader
{
	//friend class ShaderManager;
public:
	char* fileName;
	//std::vector<ShaderUniform> ShaderUniformList;
	std::vector<UniformMap> uniformMap;
	int UniformByTypeOffset[SU_COUNT];
	int sizeOfShaderUniforms;
	//BYTE* uniformDataStore;
	ID3D11Buffer* uniformBuffer;
	D3D11_INPUT_ELEMENT_DESC* varyingDescriptionList; //vertex shader only
	ID3D11InputLayout* inputLayout; //vertex shader only

	virtual ~Shader()
	{
		//SafeDeleteArray(uniformDataStore);
		uniformBuffer->Release();
		SafeDeleteArray(varyingDescriptionList);
		//ShaderUniformList.clear();
		inputLayout->Release();
		SafeDeleteArray(fileName);
	}
	const char* GetFileName()
	{
		return fileName;
	}
	virtual void Init(void* ShaderCode, unsigned long ShaderCodeLength, const char* ShaderDesc = NULL, unsigned long ShaderDescLength = 0) = 0;
	bool PushShaderUniformBuffer()
	{
		return true;
	}
	D3D11_INPUT_ELEMENT_DESC* GetVaryingDescriptionList()
	{
		return varyingDescriptionList;
	}
	ID3D11InputLayout* GetInputLayout()
	{
		return inputLayout;
	}
	//bool SetUniformBufferData(int start, int count, void* data);
	//bool SetUniformByType(UINT type, void* data);
};
class VertexShader: public Shader
{
public:
	ID3D11VertexShader *dxVS;
	VertexShader()
	{
		dxVS = NULL;
		sizeOfShaderUniforms = 0;
	}
	virtual ~VertexShader()
	{
		SafeRelease(dxVS);
	}
	//virtual bool PushShaderUniformBuffer();
	virtual void Init(void* ShaderCode, unsigned long ShaderCodeLength, const char* ShaderDesc = NULL, unsigned long ShaderDescLength = 0);
};
enum TextureID
{
	ST_2D_DIFFUSE,
	ST_COUNT
};
struct TextureMap
{
	std::string name;
	TextureID type;
	int slot;
	TextureMap(const char* iname, int islot)
	{
		name = iname;
		slot = islot;
		if(strcmp(iname, "t_diffuse") == 0)
		{
			type = ST_2D_DIFFUSE;
		}
		else
		{
			type = (TextureID)-1;
		}
	}
};
enum SamplerID
{
	SS_2D_DIFFUSE,
	SS_COUNT
};
struct SamplerMap
{
	std::string name;
	SamplerID type;
	int slot;
	SamplerMap(const char* iname, int islot)
	{
		name = iname;
		slot = islot;
		if(strcmp(iname, "s_diffuse") == 0)
		{
			type = SS_2D_DIFFUSE;
		}
		else
		{
			type = (SamplerID)-1;
		}
	}
};
class PixelShader: public Shader
{
public:
	ID3D11PixelShader *dxPS;
	std::vector<TextureMap> textureMap;
	std::vector<SamplerMap> samplerMap;
	int TextureByTypeSlot[ST_COUNT];
	int SamplerByTypeSlot[SS_COUNT];
	PixelShader()
	{
		dxPS = NULL;
		sizeOfShaderUniforms = 0;
	}
	virtual ~PixelShader()
	{
		SafeRelease(dxPS);
	}
	//virtual bool PushShaderUniformBuffer();
	virtual bool SetTexture(Texture* texture, int index = 0);
	virtual bool SetSampler(Sampler* sampler, int index = 0);
	virtual void Init(void* ShaderCode, unsigned long ShaderCodeLength, const char* ShaderDesc = NULL, unsigned long ShaderDescLength = 0);
};
class ShaderManager : public Singleton<ShaderManager>
{
	std::vector<VertexShader*> vsList;
	std::vector<PixelShader*> psList;
public:
	static void Init();
	static void Release()
	{
		SafeDelete(instance);
	}
	~ShaderManager()
	{
		for(unsigned int i = 0;i<vsList.size();i++)
		{
			SafeDelete(vsList[i]);
		}
		vsList.clear();
		for(unsigned int i = 0;i<psList.size();i++)
		{
			SafeDelete(psList[i]);
		}
		psList.clear();
	}
	//void CreateVertexShaderFromCompiledResource(char* fileName);
	void AddVertexShader(VertexShader* vs)
	{
		vsList.push_back(vs);
	}
	void AddPixelShader(PixelShader* ps)
	{
		psList.push_back(ps);
	}
	/*
	void LoadVertexShaderFromFile(Platform::String^ filename);
	void LoadPixelShaderFromFile(Platform::String^ filename);
	VertexShader* GetVertexShaderByFileName(Platform::String^ filename);
	PixelShader* GetPixelShaderByFileName(Platform::String^ filename);
	*/
	VertexShader*  LoadVertexShaderFromFile(const char* filename);
	PixelShader*  LoadPixelShaderFromFile(const char* filename);
	VertexShader* GetVertexShaderByFileName(const char* filename);
	PixelShader* GetPixelShaderByFileName(const char* filename);
};
#endif